package me.buick.util.snmp.core.dao.impl.snmp;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.Map.Entry;

import me.buick.util.snmp.core.SupportedOS;
import me.buick.util.snmp.core.Utils;
import me.buick.util.snmp.core.dao.ProcessInfoDAO;
import me.buick.util.snmp.core.pojo.CpuDetailedInfoPojo;
import me.buick.util.snmp.core.pojo.ProcessInfoPojo;

import org.opengoss.snmphibernate.api.ISnmpSession;

/**
 * 
 * Version 1.0
 * 
 * @author buick
 *
 */

public class SNMPProcessInfoDAO implements ProcessInfoDAO, IPollable{
	
	ISnmpSession session = null;
	SupportedOS  os	     = SupportedOS.Linux;
	
	List<ProcessInfoPojo> finalProcessInfoPojoList;
	Map<Integer, ProcessInfoPojo> preProcessInfoPojoRepo;
	
	int cores = 1;
	String searchCriteria = "";
	long firstCallTime = 0;
	boolean isChecked = false;
	
	public SNMPProcessInfoDAO(SupportedOS os, ISnmpSession session){
		this.session = session;
		this.os		 = os;
		cores = fetchNumberofCPUs();
	}

	public List<ProcessInfoPojo> getAllProcessInfo() {
		return getProcessInfo("");
	}
	
	public List<ProcessInfoPojo> getProcessInfo(String searchCriteria) {
		if(searchCriteria == null)
			searchCriteria = "";
		
		boolean criteriaChanged = false;
		if(!this.searchCriteria.equals(searchCriteria))
		{
			this.searchCriteria = searchCriteria;
			criteriaChanged = true;
		}		
		
		if(criteriaChanged)
		{
			Map<Integer, ProcessInfoPojo> processMap = this.getProcessesByCriteria(this.searchCriteria);
			List<ProcessInfoPojo> resultList = new ArrayList<ProcessInfoPojo>();
			for(int pid : processMap.keySet()){
				resultList.add(processMap.get(pid));
			}
			this.finalProcessInfoPojoList = resultList;
			criteriaChanged = false;
			return resultList;
		}
		
		if(this.finalProcessInfoPojoList != null)
		{
			if(this.firstCallTime == 0){
				this.firstCallTime = System.currentTimeMillis();
			}
			return this.finalProcessInfoPojoList;
		}else{
			Map<Integer, ProcessInfoPojo> processMap = this.getProcessesByCriteria(this.searchCriteria);
			List<ProcessInfoPojo> resultList = new ArrayList<ProcessInfoPojo>();
			for(int pid : processMap.keySet()){
				resultList.add(processMap.get(pid));
			}
			this.finalProcessInfoPojoList = resultList;
			return resultList;
		}
	}

	public ProcessInfoPojo getProcessInfoByPID(int pid) {
		if(!Utils.isValidatePID(pid)){
			return null;
		}
		return extractProcessInfoPojoByPID(pid);
	}

	public void poll(long interval) {
			long now = System.currentTimeMillis();
			if(this.preProcessInfoPojoRepo == null){
				this.preProcessInfoPojoRepo = getProcessesByCriteria(this.searchCriteria);
			}else{
				if((now-this.firstCallTime) > (interval*10)){
					Map<Integer, ProcessInfoPojo> processMap = getProcessesByCriteria(this.searchCriteria);
					List<ProcessInfoPojo> tempResult = new ArrayList<ProcessInfoPojo>();
					for(Entry<Integer, ProcessInfoPojo> e : processMap.entrySet()){
						ProcessInfoPojo postpj = e.getValue();
						ProcessInfoPojo prepj = preProcessInfoPojoRepo.get(postpj.getPid());
						if(prepj != null){
							double cpuPerc = 0.00;
							cpuPerc = ((postpj.getProcPerfCPUTimeInCentiSecs()*10)-(prepj.getProcPerfCPUTimeInCentiSecs()*10))/(double)interval;
							cpuPerc = cpuPerc/cores;
							if(cpuPerc >= 1.00){
								cpuPerc = 1.00/cores;
							}
							ProcessInfoPojo cppj = new ProcessInfoPojo(postpj.getPid(),postpj.getProcName(), postpj.getProcParameters(),postpj.getProcPath(),postpj.getProcPerfCPUTimeInCentiSecs(),postpj.getProcPerfMemKBytes(),postpj.getProcRunningStatus(),postpj.getProcType(),this.cores);
							cppj.setCPUPerc(cpuPerc);
							tempResult.add(cppj);
						}
					}	
					this.firstCallTime = System.currentTimeMillis();
					this.finalProcessInfoPojoList = tempResult;
					this.preProcessInfoPojoRepo = processMap;
				}else{
					if(!isChecked){
						Map<Integer, ProcessInfoPojo> processMap = getProcessesByCriteria(this.searchCriteria);
						List<ProcessInfoPojo> tempResult = new ArrayList<ProcessInfoPojo>();
						for(Entry<Integer, ProcessInfoPojo> e : processMap.entrySet()){
							ProcessInfoPojo postpj = e.getValue();
							ProcessInfoPojo prepj = preProcessInfoPojoRepo.get(postpj.getPid());
							if(prepj != null){
								double cpuPerc = 0.00;
								cpuPerc = ((postpj.getProcPerfCPUTimeInCentiSecs()*10)-(prepj.getProcPerfCPUTimeInCentiSecs()*10))/(double)interval;
								cpuPerc = cpuPerc/cores;
								if(cpuPerc >= 1.00){
									cpuPerc = 1.00/cores;
								}
								ProcessInfoPojo cppj = new ProcessInfoPojo(postpj.getPid(),postpj.getProcName(), postpj.getProcParameters(),postpj.getProcPath(),postpj.getProcPerfCPUTimeInCentiSecs(),postpj.getProcPerfMemKBytes(),postpj.getProcRunningStatus(),postpj.getProcType(),this.cores);
								cppj.setCPUPerc(cpuPerc);
								tempResult.add(cppj);
							}
						}	
						this.firstCallTime = System.currentTimeMillis();
						this.finalProcessInfoPojoList = tempResult;
						this.preProcessInfoPojoRepo = processMap;
						this.isChecked = true;
					}
					Map<Integer, ProcessInfoPojo> tempPojoMap = this.preProcessInfoPojoRepo;
					List<ProcessInfoPojo> tempResult = new ArrayList<ProcessInfoPojo>();
					for(int pid : tempPojoMap.keySet()){
						ProcessInfoPojo postpj = this.extractProcessInfoPojoByPID(pid);
						if(postpj != null){
							ProcessInfoPojo prepj = tempPojoMap.get(pid);
							double cpuPerc = 0.00;
							cpuPerc = ((postpj.getProcPerfCPUTimeInCentiSecs()*10)-(prepj.getProcPerfCPUTimeInCentiSecs()*10))/(double)interval;
							cpuPerc = cpuPerc/cores;
							if(cpuPerc >= 1.00){
								cpuPerc = 1.00/cores;
							}
							ProcessInfoPojo cppj = new ProcessInfoPojo(postpj.getPid(),postpj.getProcName(), postpj.getProcParameters(),postpj.getProcPath(),postpj.getProcPerfCPUTimeInCentiSecs(),postpj.getProcPerfMemKBytes(),postpj.getProcRunningStatus(),postpj.getProcType(),this.cores);
							cppj.setCPUPerc(cpuPerc);
							tempResult.add(cppj);
						}
					}
					this.finalProcessInfoPojoList = tempResult;
				}
			}
	}

	private Map<Integer, ProcessInfoPojo> getProcessesByCriteria(String searchCriteria) {
		Map<Integer, ProcessInfoPojo> currentProcesses = new HashMap<Integer, ProcessInfoPojo>();
		if(Utils.isEmptyString(this.searchCriteria)){
			List<ProcessInfoPojo> pl = this.extractProcessInfoPojoList();
			if(pl != null && pl.size() > 0){
				for(ProcessInfoPojo ppj : pl){
					currentProcesses.put(ppj.getPid(), ppj);
				}
			}
			return currentProcesses;
		}else{
			List<ProcessInfoPojo> ppojoList = this.extractProcessInfoPojoList();
			for(ProcessInfoPojo pojo : ppojoList){
				String pid 			= pojo.getPid()+"";
				String procName 	= pojo.getProcName();
				String procParams 	= pojo.getProcParameters();
				String procPath 	= pojo.getProcPath();
				List<String> sl = this.getCriteria(searchCriteria);
				if(sl != null){
					for(String criteria : sl){
						StringTokenizer st = new StringTokenizer(criteria, String.valueOf('\u0020'));
						while(st.hasMoreElements()){
							String s = st.nextToken().toLowerCase();
							if(pid.contains(s) || procName.contains(s) || procParams.contains(s) || procPath.contains(s)){
								ProcessInfoPojo tempPojo = currentProcesses.get(Integer.parseInt(pid));
								if(tempPojo != null){
									continue;
								}else{
									currentProcesses.put(Integer.parseInt(pid), pojo);
								}
							}
						}
					}
				}
			}
		}
		return currentProcesses;
	}
	
	private ProcessInfoPojo extractProcessInfoPojoByPID(int pid) {
		if(this.os == SupportedOS.Windows){ 
			try {
				me.buick.util.snmp.core.dao.impl.snmp.os.windows.entity.HrSWRunEntry runEntry = session.getByIndex(me.buick.util.snmp.core.dao.impl.snmp.os.windows.entity.HrSWRunEntry.class, pid);
				if(runEntry != null){
					me.buick.util.snmp.core.dao.impl.snmp.os.windows.entity.HrSWRunPerfEntry perfEntry = session.getByIndex(me.buick.util.snmp.core.dao.impl.snmp.os.windows.entity.HrSWRunPerfEntry.class, pid);
					return new ProcessInfoPojo(runEntry.getHrSWRunIndex(),runEntry.getHrSWRunName(),runEntry.getHrSWRunParameters(),runEntry.getHrSWRunPath(),perfEntry.getHrSWRunPerfCPU(),perfEntry.getHrSWRunPerfMem(),runEntry.getHrSWRunStatus(),runEntry.getHrSWRunType(),this.cores);
				}else{
					return null;
				}
			} catch (Exception e) {
				e.printStackTrace();
				return null;
			}
		}else if(this.os == SupportedOS.Linux){
			try {
				me.buick.util.snmp.core.dao.impl.snmp.os.linux.entity.HrSWRunEntry runEntry = session.getByIndex(me.buick.util.snmp.core.dao.impl.snmp.os.linux.entity.HrSWRunEntry.class, pid);
				if(runEntry != null){
					me.buick.util.snmp.core.dao.impl.snmp.os.linux.entity.HrSWRunPerfEntry perfEntry = session.getByIndex(me.buick.util.snmp.core.dao.impl.snmp.os.linux.entity.HrSWRunPerfEntry.class, pid);
					return new ProcessInfoPojo(runEntry.getHrSWRunIndex(),runEntry.getHrSWRunName(),runEntry.getHrSWRunParameters(),runEntry.getHrSWRunPath(),perfEntry.getHrSWRunPerfCPU(),perfEntry.getHrSWRunPerfMem(),runEntry.getHrSWRunStatus(),runEntry.getHrSWRunType(),this.cores);
				}else{
					return null;
				}
			} catch (Exception e) {
				return null;
			}
		}else{
			return null;
		}
	}
	
	private List<String> getCriteria(String searchCriteria){
		this.searchCriteria = searchCriteria;
		if(searchCriteria == null || searchCriteria.trim().length() <=0){
			return null;
		}
		List<String> crt = new ArrayList<String>(); 
		StringTokenizer st = new StringTokenizer(searchCriteria, ",");
		while (st.hasMoreElements()) {
			crt.add(st.nextToken().toLowerCase());
		}
		return crt;
	}
	
	private int fetchNumberofCPUs(){
		List<CpuDetailedInfoPojo> cpuList = new SNMPSystemInfoDAO(this.os, this.session).getCPUDetailed();
		if(cpuList != null){
			return cpuList.size();
		}else{
			return 1;
		}
	}

	private List<ProcessInfoPojo> extractProcessInfoPojoList() {
		List<ProcessInfoPojo> resultList = new ArrayList<ProcessInfoPojo>();
		if(this.os == SupportedOS.Windows){
			try {
				Map<Integer,me.buick.util.snmp.core.dao.impl.snmp.os.windows.entity.HrSWRunEntry> tempMap = new HashMap<Integer,me.buick.util.snmp.core.dao.impl.snmp.os.windows.entity.HrSWRunEntry>(); 
				List<me.buick.util.snmp.core.dao.impl.snmp.os.windows.entity.HrSWRunEntry> procResultList = session.getTable(me.buick.util.snmp.core.dao.impl.snmp.os.windows.entity.HrSWRunEntry.class);
				List<me.buick.util.snmp.core.dao.impl.snmp.os.windows.entity.HrSWRunPerfEntry> procPerfResultList = session.getTable(me.buick.util.snmp.core.dao.impl.snmp.os.windows.entity.HrSWRunPerfEntry.class);
				for(me.buick.util.snmp.core.dao.impl.snmp.os.windows.entity.HrSWRunEntry runEntry : procResultList){
					tempMap.put(runEntry.getHrSWRunIndex(), runEntry);
				}
				for(me.buick.util.snmp.core.dao.impl.snmp.os.windows.entity.HrSWRunPerfEntry runPerfEntry : procPerfResultList){
					me.buick.util.snmp.core.dao.impl.snmp.os.windows.entity.HrSWRunEntry runEntry = tempMap.get(runPerfEntry.getIndex());
					if(runEntry != null){
						resultList.add(new ProcessInfoPojo(runEntry.getHrSWRunIndex(),runEntry.getHrSWRunName(),runEntry.getHrSWRunParameters(),runEntry.getHrSWRunPath(),runPerfEntry.getHrSWRunPerfCPU(),runPerfEntry.getHrSWRunPerfMem(),runEntry.getHrSWRunStatus(),runEntry.getHrSWRunType(),this.cores));
					}
				}
				return resultList;
			} catch (Exception e) {
				e.printStackTrace();
				return null;
			}
		}else if(this.os == SupportedOS.Linux){
			try {
				Map<Integer,me.buick.util.snmp.core.dao.impl.snmp.os.linux.entity.HrSWRunEntry> tempMap = new HashMap<Integer,me.buick.util.snmp.core.dao.impl.snmp.os.linux.entity.HrSWRunEntry>(); 
				List<me.buick.util.snmp.core.dao.impl.snmp.os.linux.entity.HrSWRunEntry> procResultList = session.getTable(me.buick.util.snmp.core.dao.impl.snmp.os.linux.entity.HrSWRunEntry.class);
				List<me.buick.util.snmp.core.dao.impl.snmp.os.linux.entity.HrSWRunPerfEntry> procPerfResultList = session.getTable(me.buick.util.snmp.core.dao.impl.snmp.os.linux.entity.HrSWRunPerfEntry.class);
				for(me.buick.util.snmp.core.dao.impl.snmp.os.linux.entity.HrSWRunEntry runPerfEntry : procResultList){
					tempMap.put(runPerfEntry.getHrSWRunIndex(), runPerfEntry);
				}
				for(me.buick.util.snmp.core.dao.impl.snmp.os.linux.entity.HrSWRunPerfEntry perfEntry : procPerfResultList){
					me.buick.util.snmp.core.dao.impl.snmp.os.linux.entity.HrSWRunEntry runEntry = tempMap.get(perfEntry.getIndex());
					if(runEntry != null){
						resultList.add(new ProcessInfoPojo(runEntry.getHrSWRunIndex(),runEntry.getHrSWRunName(),runEntry.getHrSWRunParameters(),runEntry.getHrSWRunPath(),perfEntry.getHrSWRunPerfCPU(),perfEntry.getHrSWRunPerfMem(),runEntry.getHrSWRunStatus(),runEntry.getHrSWRunType(),this.cores));
					}
				}
				return resultList;
			} catch (Exception e) {
				e.printStackTrace();
				return null;
			}
		}else{
			return null;
		}
	}

}
